using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.NetduinoPlus;
using System.Text;

namespace Thermerators
{
    class ServerSocket
    {
        //MRM The socket listening for all incoming connections
        private static Socket socket;
        //MRM The amount of concurrent connections
        private static int MAX_CONNECTIONS = 20;
        //MRM The current id number for the next connection
        private static int idNum;
        //MRM A list of all connections
        private static ArrayList connections;
        //MRM The port to listen in on
        private static int port;

        public static int ID
        {
            get { return ServerSocket.idNum; }
            set { ServerSocket.idNum = value; }
        }

        /// <summary>
        /// MRM Begins listening for incoming connections on a given port
        /// </summary>
        /// <param name="port">The port to listen in on</param>
        public static void listen(int port1)
        {
            //MRM Create the socket
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            port = port1;
            //MRM Initialize the id number for the sockets
            ID = -1;
            //MRM Initialize the list of connections
            connections = new ArrayList();
            //MRM Begin to actually listen for any incoming connections
            (new Thread(new ThreadStart(listen))).Start();
            (new Thread(new ThreadStart(socketManager))).Start();
        }

        /// <summary>
        /// MRM Used to listen for all incoming connections
        /// </summary>
        private static void listen()
        {
            //MRM Start listening and set how many concurrent connections there can be
            socket.Bind(new IPEndPoint(IPAddress.Any, 8877));
            socket.Listen(MAX_CONNECTIONS);
            //MRM Loop indefinitly
            while (true)
            {
                Debug.Print("Connection made.");
                //MRM Accept any new client
                Socket client = socket.Accept();
                //MRM Add the new connection to the list
                connections.Add(new Connection(ID++, client));
            }
        }

        public static bool sendMessage(int id, String message)
        {
            foreach (Connection con in connections)
                if (con.getID() == id)
                {
                    Debug.Print("Sending message: " + message + "\n to socket #" + id);
                    con.sendMessage(message);
                    return true;
                }
            return false;
        }

        public static String getNextMessage(int id)
        {
            foreach (Connection con in connections)
                if (con.getID() == id)
                {
                    return con.getNextMessage();
                }
            return null;
        }

        public static ArrayList getConnectionIDs()
        {
            ArrayList conIDs = new ArrayList();
            foreach (Connection con in connections)
                conIDs.Add(con.getID());
            return conIDs;
        }

        /// <summary>
        /// Manage all socket connections and close if need be
        /// </summary>
        private static void socketManager()
        {
            //MRM Loop indefinitly
            while (true)
            {
                //MRM Loop through all sockets and close any socket that has not been active int he past 19 minutes
                for (int i = 0; i < connections.Count; i++)
                    if (ServerSocket.timeSinceEpoch() - ((Connection)connections[i]).getLastActivity() > 600000)
                    {
                        ((Connection)connections[i]).close();
                        connections.RemoveAt(i--);
                    }
                //MRM Sleep for a bit as this does not need to run often.
                Thread.Sleep(10000);
            }
        }

        /// <summary>
        /// A function to find the time in miliseconds sicne the epoch occured
        /// </summary>
        /// <returns>Time in miliseconds since epoch</returns>
        public static long timeSinceEpoch()
        {
            return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).Milliseconds;
        }
    }

    class Connection
    {
        //MRM The stream of data coming in and out of the connection
        Socket socket;
        //MRM A list of incoming messages waiting to be read
        ArrayList messages;
        //MRM The id of the connection
        int id;
        //MRM This holds the time of the last active piece from the connection
        long lastActivity;

        /// <summary>
        /// The inital constructor for the connection
        /// </summary>
        /// <param name="id">The Id number of the connection</param>
        /// <param name="stream">The stream from the socket</param>
        public Connection(int id, Socket socket)
        {
            //MRM Set the data accordingly
            this.id = id;
            this.socket = socket;
            this.lastActivity = (long)ServerSocket.timeSinceEpoch();
            this.messages = new ArrayList();
            //MRM Start a thread to listen for all incoming data
            (new Thread(new ThreadStart(readData))).Start();
        }

        /// <summary>
        /// A function to get the value of the connection id
        /// </summary>
        /// <returns>The connections ID Number</returns>
        public int getID()
        {
            return this.id;
        }

        /// <summary>
        /// Set the id for the connection
        /// </summary>
        /// <param name="id">The id of the connection</param>
        public void setID(int id)
        {
            this.id = id;
        }

        /// <summary>
        /// Get the last time the socket was considered active
        /// </summary>
        /// <returns>Last time socket was active in terms of miliseconds.</returns>
        public long getLastActivity()
        {
            return this.lastActivity;
        }

        /// <summary>
        /// Get the next message in the queue
        /// </summary>
        /// <returns>Next message</returns>
        public String getNextMessage()
        {
            if (messages.Count == 0)
                return null;
            String temp = (String)messages[0];
            messages.RemoveAt(0);
            return temp;
        }

        /// <summary>
        /// Send a message throught the socket
        /// </summary>
        /// <param name="data">The data to be sent</param>
        public void sendMessage(String data)
        {
            //MRM Make sure null data can not be sent
            if (data == null)
                return;
            //MRM Get a new encoding object to get the byte value of the data later
            System.Text.UTF8Encoding encoding = new UTF8Encoding();
            //MRM Get a byte array that is equalivilant to the data being sent
            byte[] encoded = encoding.GetBytes(data);
            //MRM Send the information
            Debug.Print("Attempting to send " + data);
            try
            {
                socket.Send(encoded);
            }
            catch (SocketException e)
            {
            }
            //MRM Reset the activity timer
            this.lastActivity = (long)ServerSocket.timeSinceEpoch();
        }

        /// <summary>
        /// Closes the socket
        /// </summary>
        public void close()
        {
            socket.Close();
        }

        /// <summary>
        /// Function to read all incoming data
        /// </summary>
        private void readData()
        {
            //MRM Loop indefinitly
            while (true)
            {
                //MRM See if there is new data to be read
                if (socket.Available > 0)
                {
                    String message = "";
                    //MRM Loop while there is still information in the buffer
                    int tempNum;
                    byte[] data = new byte[1024];
                    while ((tempNum = socket.Receive(data)) > 0)
                    {
                        for (int i = 0; i < tempNum; i++)
                            message += (char)data[i];
                        Thread.Sleep(200);
                        if (tempNum < 1024 && socket.Available == 0)
                            break;
                    }

                    try
                    {
                        Program.setCurDesiredTemp(double.Parse(message));
                    }
                    catch (System.Exception e)
                    {
                    }
                    Debug.Print(message);
                    //MRM Reset the activity timer
                    this.lastActivity = (long)ServerSocket.timeSinceEpoch();
                }
            }
        }
    }
}
